package com.gitee.wsl.ext.coroutines

import androidx.annotation.CheckResult
import androidx.annotation.MainThread
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.Lifecycle.State.STARTED
import androidx.lifecycle.LifecycleCoroutineScope
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.LifecycleOwner
import com.gitee.wsl.ioDispatcher
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.ReceiveChannel
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
import timber.log.Timber
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.resume

/**
 * 协程工具
 * <p>
 * Created by yangle on 2021/8/10.
 * Website：http://www.yangle.tech
 */
object LifecycleExt{


    /**
     * 当前线程挂起一段时间，执行后续逻辑，不阻塞当前线程
     *
     * @param context CoroutineContext
     * @param timeMillis 挂起时间|毫秒
     * @param exec 执行方法
     * @return Job
     * @sample
     * coroutineDelay(coroutineContext, 100) {
     *      // 执行逻辑
     * }
     */
    fun coroutineDelay(
        context: CoroutineContext, timeMillis: Long,
        exec: () -> Unit
    ): Job {
        val scope = CoroutineScope(context)
        return scope.launch(getExceptionHandler("coroutineDelay")) {
            delay(timeMillis)
            exec()
        }
    }

    /**
     * 当前线程挂起一段时间，执行后续逻辑，不阻塞当前线程
     * 在Activity、Fragment中使用此方法，与页面生命周期绑定
     *
     * @param scope LifecycleCoroutineScope
     * @param timeMillis 挂起时间|毫秒
     * @param exec 执行方法
     * @return Job
     * @sample
     * coroutineDelay(lifecycleScope, 100) {
     *      // 执行逻辑
     * }
     */
    fun coroutineDelay(
        scope: LifecycleCoroutineScope, timeMillis: Long,
        exec: () -> Unit
    ): Job = scope.launch(getExceptionHandler("coroutineDelay-Lifecycle")) {
        delay(timeMillis)
        exec()
    }

    /**
     * 当前线程周期运行任务
     *
     * @param context CoroutineContext
     * @param timeMillis 运行周期|毫秒
     * @param exec 执行方法
     * @return Job
     * @sample
     * val job = coroutineInterval(coroutineContext, 1000) {
     *      // 执行逻辑
     * }
     * job.cancel()
     */
    fun coroutineInterval(
        context: CoroutineContext, timeMillis: Long,
        exec: (time: Int) -> Unit
    ): Job {
        val scope = CoroutineScope(context)
        return scope.launch(getExceptionHandler("coroutineInterval")) {
            repeat(Int.MAX_VALUE) {
                exec(it)
                delay(timeMillis)
            }
        }
    }

    /**
     * 当前线程周期运行任务
     * 在Activity、Fragment中使用此方法，与页面生命周期绑定
     *
     * @param scope LifecycleCoroutineScope
     * @param timeMillis 运行周期|毫秒
     * @param exec 执行方法
     * @return Job
     * @sample
     * val job = coroutineInterval(lifecycleScope, 1000) {
     *      // 执行逻辑
     * }
     * job.cancel()
     */
    fun coroutineInterval(
        scope: LifecycleCoroutineScope, timeMillis: Long,
        exec: (time: Int) -> Unit
    ): Job = scope.launch(getExceptionHandler("coroutineInterval-Lifecycle")) {
        repeat(Int.MAX_VALUE) {
            exec(it)
            delay(timeMillis)
        }
    }

    /**
     * 在子线程协程中执行任务，在主线程协程中处理结果，不阻塞当前线程
     *
     * @param context CoroutineContext
     * @param worker 子线程协程
     * @param main 主线程协程
     * @sample
     * coroutineSchedule(coroutineContext, worker = {
     *      // 耗时逻辑
     * }, main = {
     *      // 处理结果
     *  })
     */
    fun coroutineSchedule(
        context: CoroutineContext, worker: () -> Unit,
        main: () -> Unit
    ) {
        val scope = CoroutineScope(context)
        scope.launch(Dispatchers.ioDispatcher + getExceptionHandler("coroutineSchedule")) {
            worker()
            withContext(Dispatchers.Main) {
                main()
            }
        }
    }

    /**
     * 在子线程协程中执行任务，在主线程协程中处理结果，不阻塞当前线程
     * 在Activity、Fragment中使用此方法，与页面生命周期绑定
     *
     * @param scope LifecycleCoroutineScope
     * @param worker 子线程协程
     * @param main 主线程协程
     * @sample
     * coroutineSchedule(lifecycleScope, worker = {
     *      // 耗时逻辑
     * }, main = {
     *      // 处理结果
     *  })
     */
    fun coroutineSchedule(
        scope: LifecycleCoroutineScope, worker: () -> Unit,
        main: () -> Unit
    ) = scope.launch(Dispatchers.ioDispatcher + getExceptionHandler("coroutineSchedule-Lifecycle")) {
        worker()
        withContext(Dispatchers.Main) {
            main()
        }
    }

    /**
     * 协程异常处理
     *
     * @param method 方法名
     */
    fun getExceptionHandler(method: String): CoroutineExceptionHandler {
        return CoroutineExceptionHandler { _, exception ->
            Timber.d("[$method] Exception:${exception}")
        }
    }
}


fun LifecycleOwner.doOnLifecycle(
    onCreate: (() -> Unit)? = null,
    onStart: (() -> Unit)? = null,
    onResume: (() -> Unit)? = null,
    onPause: (() -> Unit)? = null,
    onStop: (() -> Unit)? = null,
    onDestroy: (() -> Unit)? = null,
) =
    lifecycle.addObserver(object : DefaultLifecycleObserver {
        override fun onCreate(owner: LifecycleOwner) {
            onCreate?.invoke()
        }

        override fun onStart(owner: LifecycleOwner) {
            onStart?.invoke()
        }

        override fun onResume(owner: LifecycleOwner) {
            onResume?.invoke()
        }

        override fun onPause(owner: LifecycleOwner) {
            onPause?.invoke()
        }

        override fun onStop(owner: LifecycleOwner) {
            onStop?.invoke()
        }

        override fun onDestroy(owner: LifecycleOwner) {
            onDestroy?.invoke()
        }
    })

/*fun LifecycleOwner.tickRun(timeInternal: Long = TimeTaskServer.PROGRESS_TICK_INTERNAL, timeLate:Long= TimeTaskServer.PROGRESS_UPDATE_INITIAL_INTERVAL, callback: TimeTaskScope.()->Unit){
    TimeTaskServer.tickRun(this,timeInternal,timeLate,taskRun=callback)
}*/

/** Suspend until [Lifecycle.getCurrentState] is at least [STARTED] */
@MainThread
suspend fun Lifecycle.awaitStarted() {
    // Fast path: we're already started.
    if (currentState.isAtLeast(STARTED)) return

    // Slow path: observe the lifecycle until we're started.
    var observer: LifecycleObserver? = null
    try {
        suspendCancellableCoroutine { continuation ->
            observer = object : DefaultLifecycleObserver {
                override fun onStart(owner: LifecycleOwner) {
                    continuation.resume(Unit)
                }
            }
            addObserver(observer!!)
        }
    } finally {
        // 'observer' will always be null if this method is marked as 'inline'.
        observer?.let(::removeObserver)
    }
}

/** Remove and re-add the observer to ensure all its lifecycle callbacks are invoked. */
@MainThread
fun Lifecycle.removeAndAddObserver(observer: LifecycleObserver) {
    removeObserver(observer)
    addObserver(observer)
}


/**
 * Perform an action on any [lifecycle][Lifecycle] event change.
 *
 * @param scope Root coroutine scope
 * @param capacity Capacity of the channel's buffer (no buffer by default)
 * @param action An action to perform
 */
/*fun Lifecycle.events(
    scope: CoroutineScope,
    capacity: Int = Channel.RENDEZVOUS,
    action: suspend (Lifecycle.Event) -> Unit,
) {
    val events = scope.actor<Lifecycle.Event>(Dispatchers.Main.immediate, capacity) {
        for (event in channel) action(event)
    }

    val observer = observer(scope, events::trySend)
    addObserver(observer)
    events.invokeOnClose { removeObserver(observer) }
}*/

/**
 * Perform an action on any [lifecycle][Lifecycle] event change, inside new [CoroutineScope].
 *
 * @param capacity Capacity of the channel's buffer (no buffer by default)
 * @param action An action to perform
 */
/*suspend fun Lifecycle.events(
    capacity: Int = Channel.RENDEZVOUS,
    action: suspend (Lifecycle.Event) -> Unit,
) = coroutineScope {
    events(this, capacity, action)
}*/

/**
 * Create a channel which emits on any [lifecycle][Lifecycle] event change.
 *
 * Example:
 *
 * ```
 * launch {
 *      lifecycle.events()
 *          .consumeEach { /* handle lifecycle event change */ }
 * }
 * ```
 *
 * @param scope Root coroutine scope
 * @param capacity Capacity of the channel's buffer (no buffer by default)
 */
fun Lifecycle.events(
    scope: CoroutineScope,
    capacity: Int = Channel.RENDEZVOUS,
): ReceiveChannel<Lifecycle.Event> = corbindReceiveChannel(capacity) {
    val observer = observer(scope, ::trySend)
    addObserver(observer)
    invokeOnClose { removeObserver(observer) }
}

/**
 * Create a flow which emits on any [lifecycle][Lifecycle] event change.
 *
 * Example:
 *
 * ```
 * lifecycle.events()
 *      .onEach { /* handle lifecycle event change */ }
 *      .flowWithLifecycle(lifecycle)
 *      .launchIn(lifecycleScope) // lifecycle-runtime-ktx
 * ```
 */
@CheckResult
fun Lifecycle.events(): Flow<Lifecycle.Event> = channelFlow {
    val observer = observer(this, ::trySend)
    addObserver(observer)
    awaitClose { removeObserver(observer) }
}

@CheckResult
private fun observer(
    scope: CoroutineScope,
    emitter: (Lifecycle.Event) -> Unit,
) = LifecycleEventObserver { _, event ->
    if (scope.isActive) emitter(event)
}

